Strict

Import mojo
Import vector2d
Import ball
Import diddy
Import model
Import scoreStatusBar
Import wrongRight

Class BonusBallBounceScreen Extends Screen Abstract
	Const GRAVITY:Float = 0.3
	
	Const NORMAL_STATE:Int = 0
	Const END_ROUND_STATE:Int = 1
	Const START_ROUND_STATE:Int = 2
	Const START_STATE:Int = 3
	Const GAME_OVER_STATE:Int = 4
	Const CREATE_STATE:Int = 5
	
	Const SCORE_STAR:Int = 100
	Const SCORE_TIME_LEFT:Int = 10
	
	Field ballsList:ArrayList<Ball> = New ArrayList<Ball>
	Field wrongRightList:ArrayList<WrongRight> = New ArrayList<WrongRight>
	
	Field wrongRightNdx:Int=0
	Field wrongRightMax:Int=4
	
	Field scoreStatusBar:ScoreStatusBar
	Field roundNumber:int = 0
	Field timer:Int = 0
	Field pointsToMax:Float = 12 ' Best if this is divisible by 3
	Field realMilliSecs1:Int
	Field realMilliSecs2:Int
	
	Field bgImage:GameImage
	Field glow1:GameImage
	
	Field ballAlpha:Float=1
	Field getReadyX:Float=0
	
	Field gameState:Int = CREATE_STATE
	Field nextGameState:Int = START_STATE
	
	Field timeLastStateChanged:Int = 0
	Field timeSinceLastStateChange:Int = 0
	
	Field endLevelFadeTween:Tween2= New Tween2(Tween2.Cubic.EaseInOut,1,0,2000)
	Field newLevelFadeTween:Tween2= New Tween2(Tween2.Cubic.EaseInOut,0,1,1000)
	
	Field getReadyTween1:Tween2= New Tween2(Tween2.Elastic.EaseInOut,SCREEN_WIDTH+100,SCREEN_WIDTH2,1000)
	Field getReadyTween2:Tween2= New Tween2(Tween2.Elastic.EaseInOut,SCREEN_WIDTH2,-100,1000)
	Field currentGetReadyTween:Int=0
	
	'***************************
	'
	'
	Method New()
		
		glow1 = game.images.Find("spark")	
		
		' Set up the wrongright cache		
		For Local ndx:Int=0 Until wrongRightMax
			wrongRightList.Add( new WrongRight() ) 
		Next	
	End
	
	'***************************
	'
	'
 	Method Start:Void()	
		scoreStatusBar = New ScoreStatusBar(SCREEN_WIDTH-40, 10, SCREEN_HEIGHT-20, pointsToMax)
		ChangeGameState(START_STATE)
		
		GetNextGeneration()
		timer = GetMaxTimer()
		realMilliSecs1=RealMillisecs() 'millisecs
		
		scoreStatusBar.Reset()

    End     
	
	  
	'***************************
	'
	'
    Method Update:Void()
		'
		' Update state change, state changes are saved for the next update
		'
		UpdateState()
		
		If KeyHit(KEY_ESCAPE)
			game.screenFade.Start(50, True)
			game.nextScreen = _model._mainMenuScreen			
		End
		
		'
		' message window
		'
		_model.messageWindow.Update()
		
		'
		' Update Balls
		'
		if(gameState <> START_STATE) 
			For Local ndx:Int=0 Until GetMaxBalls()
				Local tempBall:Ball = ballsList.Get(ndx)
		    	tempBall.Update()								
			next
		endif
		
		'
		' Check for collisions
		'
		checkCollisions()
			
		'
		' Update particles
		'
		Particle.UpdateAll()			
		
		'
		' Render WrongRights
		'
		For Local ndx:Int=0 Until wrongRightMax
			Local wrongRight:WrongRight = wrongRightList.Get(ndx)
	    	wrongRight.Update()								
		next
		
		'
		' status bars
		'
		scoreStatusBar.Update()
			
		
		'		
		' State-specific stuff
		'
		if((gameState = NORMAL_STATE) or (gameState = START_ROUND_STATE) )							
			'
			' Check if we have finished this round
			'
			if(GetRegenerationCounter()<=0)
				ChangeGameState(END_ROUND_STATE)		
			endif
			
			'
			' Work out when 1 second has elapsed and update timer
			'
			realMilliSecs2=RealMillisecs()
			if(realMilliSecs2>realMilliSecs1+1000)
				timer-=1
				realMilliSecs1=RealMillisecs()
			endif
			
			'
			' Test for mouse to ball collisions
			'
			For Local ndx:Int=0 Until GetMaxBalls()
			
				Local tempBall:Ball = ballsList.Get(ndx)
									
				If (tempBall.alive and TouchHit())
					if(checkMouseHit(tempBall))
						doMouseHit(tempBall)
						For Local i% = 1 To 5
							Particle.Create(glow1, game.mouseX , game.mouseY, Rnd(-3,3), Rnd(-3,3), 0, 2000)
						Next
					endif
				End
				
			next
			
			'
			' Check for game over
			'
			if (scoreStatusBar.GetSelectedStars() = scoreStatusBar.STARS_MAX)
				ChangeGameState(GAME_OVER_STATE)			
			elseif (timer<=0)
				ChangeGameState(GAME_OVER_STATE)
			endif
		endif
		
		'
		' Set ball alpha value
		'
		if(gameState = NORMAL_STATE)	
			ballAlpha=1
		elseif(gameState = END_ROUND_STATE)		
			endLevelFadeTween.Update()
			ballAlpha=endLevelFadeTween.Value()
		elseif(gameState = START_ROUND_STATE)	
			newLevelFadeTween.Update()
			ballAlpha=newLevelFadeTween.Value()	
		endif
		
		'
		' State specific processing
		'		
		if(gameState = END_ROUND_STATE)								
				if(endLevelFadeTween.isActive=false)
					ChangeGameState(START_ROUND_STATE)
				End
		endif
		
		if(gameState = START_ROUND_STATE)	
			if(currentGetReadyTween=1)
				getReadyTween1.Update()
				getReadyX=getReadyTween1.Value()
			elseif(currentGetReadyTween=2)
				getReadyTween2.Update()
				getReadyX=getReadyTween2.Value()
			End
			
			if(getReadyTween1.isActive=false and currentGetReadyTween=1)
				currentGetReadyTween=2
				getReadyTween2.Start()
			End
			
			if((newLevelFadeTween.isActive=false and TouchHit()) or timeSinceLastStateChange>3000)
				ChangeGameState(NORMAL_STATE)
			Endif
		endif
		
		'if(gameState = GET_READY_STATE)	
		if(gameState = GAME_OVER_STATE)
		
		End

    End
	
	'***************************
	'
	'
   	Method Render:Void()
		Cls 0,0,0
		
		'
		' Background
		'
		bgImage.Draw(SCREEN_WIDTH2, SCREEN_HEIGHT2)
		
		
		'
		' Render WrongRights
		'
		For Local ndx:Int=0 Until wrongRightMax
			Local wrongRight:WrongRight = wrongRightList.Get(ndx)
	    	wrongRight.Draw()								
		next
		
   		' 
   		' Render balls
   		'		
    	if(gameState <> START_STATE) 
	   		For Local ndx:Int=0 Until GetMaxBalls()
				Local tempBall:Ball = ballsList.Get(ndx)
				tempBall.alpha=ballAlpha
		    	tempBall.Draw()
			next
		endif
		SetAlpha(1)
		
		'
		' status bars
		'
		scoreStatusBar.Draw()
		
		'
		' message window
		'
		_model.messageWindow.Draw()
		
		
		SetColor(255,255,255)
	'	_model.font.DrawText ("Round: "+roundNumber, 100, 10, AngelFont.ALIGN_CENTER)
	'	_model.font.DrawText ("Time: "+timer, SCREEN_WIDTH-200,10, AngelFont.ALIGN_CENTER)
		DrawText ("ROUND: "+roundNumber, 100, 100.5,0.5)
		DrawText ("TIME: "+timer, SCREEN_WIDTH-200,10,0.5,0.5)
		FPSCounter.Draw(0,50)
		
		'
		' Render Particles
		'
		Particle.DrawAll()			
		
		'
		' State-specific stuff
		'
		Select(gameState)
			Case NORMAL_STATE
			Case END_ROUND_STATE
				_model.font.DrawText("ROUND "+roundNumber+" CLEAR!", SCREEN_WIDTH2, SCREEN_HEIGHT2, AngelFont.ALIGN_CENTER)
			Case START_ROUND_STATE
				_model.font.DrawText("ROUND "+roundNumber, SCREEN_WIDTH2, SCREEN_HEIGHT2, AngelFont.ALIGN_CENTER)
				_model.font.DrawText("GET READY!", getReadyX, SCREEN_HEIGHT2+30, AngelFont.ALIGN_CENTER)
				
			Case START_STATE
			Case GAME_OVER_STATE			
				
		End
    End
	
	'***************************
	'
	'
	Method PostFadeOut:Void()
		Particle.Clear()
		Super.PostFadeOut()
	End
	
	'***************************
	'
	'
	Method checkMouseHit:bool(ball:Ball)
		if ((game.mouseX>ball.position.getX()-ball.getRadius()) and (game.mouseX<ball.position.getX()+ball.getRadius()) and		
			(game.mouseY>ball.position.getY()-ball.getRadius()) and (game.mouseY<ball.position.getY()+ball.getRadius()))
			return true
		End
		
		return false
	End
	
	'***************************
	'
	'
	Method checkCollisions:void()
		ballsList.Sort()

		' Check for collision with walls
		For Local ndx:Int=0 Until GetMaxBalls()
	
			Local ball1:Ball = ballsList.Get(ndx)
			
			if(ball1.alive = false)
				continue
			endif
			
			if (ball1.position.getX() - ball1.getRadius() < 0)			
				ball1.position.setX(ball1.getRadius()) ' Place ball against edge
				ball1.velocity.setX(-(ball1.velocity.getX() * Ball.restitution)) ' Reverse direction and account for friction
				ball1.velocity.setY(ball1.velocity.getY() * Ball.restitution)
				ball1.DoCollisionAnim()
			elseif (ball1.position.getX() + ball1.getRadius() > SCREEN_WIDTH) ' Right Wall
			
				ball1.position.setX(SCREEN_WIDTH - ball1.getRadius())		' Place ball against edge
				ball1.velocity.setX(-(ball1.velocity.getX() * Ball.restitution)) ' Reverse direction and account for friction
				ball1.velocity.setY((ball1.velocity.getY() * Ball.restitution))
				ball1.DoCollisionAnim()
			endif

			if (ball1.position.getY() - ball1.getRadius() < 0)				' Top Wall
				ball1.position.setY(ball1.getRadius())				' Place ball against edge
				ball1.velocity.setY(-(ball1.velocity.getY() * Ball.restitution)) ' Reverse direction and account for friction
				ball1.velocity.setX((ball1.velocity.getX() * Ball.restitution))	
				ball1.DoCollisionAnim()		
			elseif (ball1.position.getY() + ball1.getRadius() > SCREEN_HEIGHT) ' Bottom Wall	
				ball1.position.setY(SCREEN_HEIGHT - ball1.getRadius())		' Place ball against edge
				ball1.velocity.setY(-(ball1.velocity.getY() * Ball.restitution))    ' Reverse direction and account for friction
				ball1.velocity.setX((ball1.velocity.getX() * Ball.restitution))
				ball1.DoCollisionAnim()
			endif

			' Ball to Ball collision
			For Local ndx2:Int=ndx+1 Until GetMaxBalls()	
				Local ball2:Ball = ballsList.Get(ndx2)	
				if ((ball1.position.getX() + ball1.getRadius()) < (ball2.position.getX() - ball2.getRadius()))
						exit
				endif

				if((ball1.position.getY() + ball1.getRadius()) < (ball2.position.getY() - ball2.getRadius()) or
				   (ball2.position.getY() + ball2.getRadius()) < (ball1.position.getY() - ball1.getRadius()))
				   		Continue
				endif
				
				if(ball2.alive=false)
					Continue
				End
				
				ball1.resolveCollision(ball2)
			next
		next
	End
	
	Method UpdateState:void()
		if(nextGameState<>gameState)
			ChangeGameStateNextUpdate(nextGameState)
		End
		
		'
		' Time since state changed
		timeSinceLastStateChange = RealMillisecs()-timeLastStateChanged
	End
	
	Method ChangeGameState:void(newState:Int)
		nextGameState=newState
	End
	
	'***************************
	'
	'
	Method ChangeGameStateNextUpdate:void(newState:Int)
		Print "Changing state from "+gameState+"to : "+newState
		timeLastStateChanged=RealMillisecs()
		gameState=newState	
		Select(gameState)
			Case NORMAL_STATE
				
			Case END_ROUND_STATE
				initEndRoundState()
			Case START_ROUND_STATE	
				initStartRoundState()
			Case START_STATE
				initStartState()
			Case GAME_OVER_STATE
				initGameOverState()
		End
	End
	
	'***************************
	'
	'
	Method initStartState:void()
		_model.messageWindow.Show(GetInstructions(), new StartGameMessageCallback(self))
	End
	
	'***************************
	'
	'
	Method initEndRoundState:void()
		endLevelFadeTween.Rewind()
		endLevelFadeTween.Start()
	End
	
	'***************************
	'
	'
	Method initStartRoundState:void()
		ballsList.Clear()
		Particle.Clear()	
		newLevelFadeTween.Start()
		currentGetReadyTween=1
		getReadyTween1.Start()
		roundNumber+=1
		
		SetRoundAttributes()
		GetNextGeneration()
	End
	
	'***************************
	'
	'
	Method initGameOverState:void()
		Local message:String
		Local score:Int
		
		score = scoreStatusBar.GetSelectedStars() * SCORE_STAR
		
		if(timer=0)
			message = "OUT OF TIME;;"
		else
			score += timer*SCORE_TIME_LEFT
			message = "LEVEL CLEARED;;"
		End
		message += "YOU GOT "+scoreStatusBar.GetSelectedStars()+" STARS;;"
		message += "SCORE: "+score
		
		_model.stars += scoreStatusBar.GetSelectedStars()
		_model.score += score
		_model.messageWindow.Show(message, new EndGameMessageCallback())
	End
	
	'***************************
	'
	'
	Method DoWrongRight:void(x:Int, y:Int, isRight:Bool)
		Local wrongRight:WrongRight 
		
		wrongRight = wrongRightList.Get(wrongRightNdx)
		wrongRightNdx+=1
		
		if(wrongRightNdx=wrongRightMax)
			wrongRightNdx=0
		End

		wrongRight.Start(x,y, isRight)
	End
	
	' *********************************************
	' Virtual Methods
	'
	Method doMouseHit:void(ball:Ball) Abstract
	Method GetMaxBalls:Int() Abstract
	Method GetRegenerationCounter:Int() Abstract	
	Method GetMaxTimer:Int() Abstract
	Method GetNextGeneration:void()	Abstract
	Method SetRoundAttributes:void() Abstract
	Method GetInstructions:String() Abstract
End



Class StartGameMessageCallback Implements WindowCallback
	Field bonusBallBounceScreen:BonusBallBounceScreen
	
	Method New(bonusBallBounceScreen:BonusBallBounceScreen)
		self.bonusBallBounceScreen = bonusBallBounceScreen
	End
	
	Method onClose:Void()
		bonusBallBounceScreen.ChangeGameState(bonusBallBounceScreen.START_ROUND_STATE)
	End
End

Class EndGameMessageCallback Implements WindowCallback
	
	Method onClose:Void()

		_model.bonusGameSuccess=true
		_model.bonusGameFailed=false
		game.screenFade.Start(50, True)
		game.nextScreen = _model._mazeScreen
	End
End

